#pragma once

#include "funpl.h"
#include "ast.h"
#include <vector>

namespace funpl
{

namespace ast
{

/// Statement node.
struct statement : public node
{
protected:
	/// Constructor.
	statement(node_type::t type, node *parent = nullptr)
		: node(type, parent) { }
};

/// Expression node.
struct expression : public statement
{
protected:
	/// Constructor.
	expression(node_type::t type, node *parent = nullptr)
		: statement(type, parent) { }
};

/// Compound statement node.
struct compound : public expression
{
	util::const_char_range name;

	typedef std::vector<statement*> statement_vector; // TODO: factor out
	statement_vector statements;

	/// Constructor.
	compound(node *parent = nullptr,
		const util::const_char_range &name = util::empty_const_char_range())
			: expression(node_type::compound, parent),
			name(name) { }
};

/// Function class.
namespace variable_class
{
	/// Enumeration.
	enum t
	{
		normal,
		copy,
		output,
		destructible
	};
}

struct type;

/// Variable definition statement node.
struct variable : public statement
{
	variable_class::t variableClass;
	util::const_char_range name;

	type *type;

	expression *init;

	/// Constructor.
	variable(node *parent = nullptr,
		variable_class::t variableClass = variable_class::normal,
		const util::const_char_range &name = util::empty_const_char_range())
			: statement(node_type::variable, parent),
			variableClass(variableClass),
			name(name),
			type(),
			init() { }
};

/// If statement node.
struct if_statement : public expression
{
	expression *condition;
	expression *consequence;
	expression *alternative;

	/// Constructor.
	if_statement(node *parent = nullptr,
		expression *condition = nullptr,
		expression *consequence = nullptr)
			: expression(node_type::if_statement, parent),
			condition(condition),
			consequence(consequence),
			alternative() { }
};

/// For statement node.
struct for_statement : public statement
{
	typedef std::pair<util::const_char_range, variable*> definition;
	typedef std::vector<definition> definition_vector;
	definition_vector definitions;

	typedef std::pair<util::const_char_range, expression*> range;
	typedef std::vector<range> range_vector;
	range_vector ranges;

	expression *condition;
	expression *step;
	expression *loop;

	/// Constructor.
	for_statement(node *parent = nullptr)
		: statement(node_type::for_statement, parent),
		condition(),
		step(),
		loop() { }
};

/// While statement node.
struct while_statement : public statement
{
	expression *condition;
	expression *loop;

	/// Constructor.
	while_statement(node *parent = nullptr,
		expression *condition = nullptr,
		expression *loop = nullptr)
			: statement(node_type::while_statement, parent),
			condition(condition),
			loop(loop) { }
};

/// Do-while statement node.
struct do_while_statement : public statement
{
	expression *condition;
	expression *loop;

	/// Constructor.
	do_while_statement(node *parent = nullptr,
		expression *condition = nullptr,
		expression *loop = nullptr)
			: statement(node_type::do_while_statement, parent),
			condition(condition),
			loop(loop) { }
};

/// Catch statement node.
struct catch_statement : public statement
{
	variable *exception;
	expression *handling;

	catch_statement *next;

	/// Constructor.
	catch_statement(node *parent = nullptr,
		variable *exception = nullptr,
		expression *handling = nullptr)
			: statement(node_type::catch_statement, parent),
			exception(exception),
			handling(handling),
			next() { }
};

/// Try statement node.
struct try_statement : public expression
{
	expression *statements;

	catch_statement *handlers;

	/// Constructor.
	try_statement(node *parent = nullptr,
		expression *statements = nullptr)
			: expression(node_type::try_statement, parent),
			statements(statements),
			handlers() { }
};

/// Throw statement.
struct throw_statement : public statement
{
	expression *exception;

	/// Constructor.
	throw_statement(node *parent = nullptr,
		expression *exception = nullptr)
			: statement(node_type::throw_statement, parent),
			exception(exception) { }
};

/// Return statement.
struct return_statement : public statement
{
	expression *value;

	/// Constructor.
	return_statement(node *parent = nullptr,
		expression *value = nullptr)
			: statement(node_type::return_statement, parent),
			value(value) { }
};

struct type;
struct arguments;

/// New expression.
struct new_expression : public expression
{
	type *constructor;
	arguments *constructorArgs;

	/// Constructor.
	new_expression(node *parent = nullptr,
		type *constructor = nullptr,
		arguments *constructorArgs = nullptr)
			: expression(node_type::new_expression, parent),
			constructor(constructor),
			constructorArgs(constructorArgs) { }
};

/// Delete expression.
struct delete_expression : public expression
{
	expression *operand;

	/// Constructor.
	delete_expression(node *parent = nullptr,
		expression *operand = nullptr)
			: expression(node_type::delete_expression, parent),
			operand(operand) { }
};

/// Guard expression.
struct guard_expression : public expression
{
	util::const_char_range name;

	expression *guard;

	/// Constructor.
	guard_expression(node *parent = nullptr,
		const util::const_char_range &name = util::empty_const_char_range(),
		expression *guard = nullptr)
			: expression(node_type::guard_expression, parent),
			name(name),
			guard(guard) { }
};

/// Unguard expression.
struct unguard_expression : public expression
{
	util::const_char_range name;

	/// Constructor.
	unguard_expression(node *parent = nullptr,
		const util::const_char_range &name = util::empty_const_char_range())
			: expression(node_type::unguard_expression, parent),
			name(name) { }
};

} // namespace

} // namespace